ad4b0d2ffe096ed22b1321bb829ea99051e4eb2f
[lhc/web/wiklou.git] / includes / installer / WebInstaller.php
1 <?php
2
3 class WebInstaller extends Installer {
4 /** WebRequest object */
5 public $request;
6
7 /** Cached session array */
8 public $session;
9
10 /** Captured PHP error text. Temporary.
11 */
12 public $phpErrors;
13
14 /**
15 * The main sequence of page names. These will be displayed in turn.
16 * To add one:
17 * * Add it here
18 * * Add a config-page-<name> message
19 * * Add a WebInstaller_<name> class
20 */
21 public $pageSequence = array(
22 'Language',
23 'Welcome',
24 'DBConnect',
25 'Upgrade',
26 'DBSettings',
27 'Name',
28 'Options',
29 'Install',
30 'Complete',
31 );
32
33 /**
34 * Out of sequence pages, selectable by the user at any time.
35 */
36 public $otherPages = array(
37 'Restart',
38 'Readme',
39 'ReleaseNotes',
40 'Copying',
41 'UpgradeDoc', // Can't use Upgrade due to Upgrade step
42 );
43
44 /**
45 * Array of pages which have declared that they have been submitted, have validated
46 * their input, and need no further processing.
47 */
48 public $happyPages;
49
50 /**
51 * List of "skipped" pages. These are pages that will automatically continue
52 * to the next page on any GET request. To avoid breaking the "back" button,
53 * they need to be skipped during a back operation.
54 */
55 public $skippedPages;
56
57 /**
58 * Flag indicating that session data may have been lost.
59 */
60 public $showSessionWarning = false;
61
62 public $helpId = 0;
63 public $tabIndex = 1;
64
65 public $currentPageName;
66
67 /** Constructor */
68 public function __construct( $request ) {
69 parent::__construct();
70 $this->output = new WebInstallerOutput( $this );
71 $this->request = $request;
72 }
73
74 /**
75 * Main entry point.
76 *
77 * @param $session Array: initial session array
78 *
79 * @return Array: new session array
80 */
81 public function execute( $session ) {
82 $this->session = $session;
83 if ( isset( $session['settings'] ) ) {
84 $this->settings = $session['settings'] + $this->settings;
85 }
86 $this->exportVars();
87 $this->setupLanguage();
88
89 if( $this->getVar( '_InstallDone' ) && $this->request->getVal( 'localsettings' ) )
90 {
91 $ls = new LocalSettingsGenerator( $this );
92 $this->request->response()->header('Content-type: text/plain');
93 $this->request->response()->header(
94 'Content-Disposition: attachment; filename="LocalSettings.php"'
95 );
96 echo $ls->getText();
97 return $this->session;
98 }
99
100 if ( isset( $session['happyPages'] ) ) {
101 $this->happyPages = $session['happyPages'];
102 } else {
103 $this->happyPages = array();
104 }
105 if ( isset( $session['skippedPages'] ) ) {
106 $this->skippedPages = $session['skippedPages'];
107 } else {
108 $this->skippedPages = array();
109 }
110 $lowestUnhappy = $this->getLowestUnhappy();
111
112 # Special case for Creative Commons partner chooser box
113 if ( $this->request->getVal( 'SubmitCC' ) ) {
114 $page = $this->getPageByName( 'Options' );
115 $this->output->useShortHeader();
116 $page->submitCC();
117 return $this->finish();
118 }
119 if ( $this->request->getVal( 'ShowCC' ) ) {
120 $page = $this->getPageByName( 'Options' );
121 $this->output->useShortHeader();
122 $this->output->addHTML( $page->getCCDoneBox() );
123 return $this->finish();
124 }
125
126 # Get the page name
127 $pageName = $this->request->getVal( 'page' );
128
129 if ( in_array( $pageName, $this->otherPages ) ) {
130 # Out of sequence
131 $pageId = false;
132 $page = $this->getPageByName( $pageName );
133 } else {
134 # Main sequence
135 if ( !$pageName || !in_array( $pageName, $this->pageSequence ) ) {
136 $pageId = $lowestUnhappy;
137 } else {
138 $pageId = array_search( $pageName, $this->pageSequence );
139 }
140
141 # If necessary, move back to the lowest-numbered unhappy page
142 if ( $pageId > $lowestUnhappy ) {
143 $pageId = $lowestUnhappy;
144 if ( $lowestUnhappy == 0 ) {
145 # Knocked back to start, possible loss of session data
146 $this->showSessionWarning = true;
147 }
148 }
149 $pageName = $this->pageSequence[$pageId];
150 $page = $this->getPageByName( $pageName );
151 }
152
153 # If a back button was submitted, go back without submitting the form data
154 if ( $this->request->wasPosted() && $this->request->getBool( 'submit-back' ) ) {
155 if ( $this->request->getVal( 'lastPage' ) ) {
156 $nextPage = $this->request->getVal( 'lastPage' );
157 } elseif ( $pageId !== false ) {
158 # Main sequence page
159 # Skip the skipped pages
160 $nextPageId = $pageId;
161 do {
162 $nextPageId--;
163 $nextPage = $this->pageSequence[$nextPageId];
164 } while( isset( $this->skippedPages[$nextPage] ) );
165 } else {
166 $nextPage = $this->pageSequence[$lowestUnhappy];
167 }
168 $this->output->redirect( $this->getUrl( array( 'page' => $nextPage ) ) );
169 return $this->finish();
170 }
171
172 # Execute the page
173 $this->currentPageName = $page->getName();
174 $this->startPageWrapper( $pageName );
175 $localSettings = $this->getLocalSettingsStatus();
176 if( !$localSettings->isGood() ) {
177 $this->showStatusBox( $localSettings );
178 $result = 'output';
179 } else {
180 $result = $page->execute();
181 }
182 $this->endPageWrapper();
183
184 if ( $result == 'skip' ) {
185 # Page skipped without explicit submission
186 # Skip it when we click "back" so that we don't just go forward again
187 $this->skippedPages[$pageName] = true;
188 $result = 'continue';
189 } else {
190 unset( $this->skippedPages[$pageName] );
191 }
192
193 # If it was posted, the page can request a continue to the next page
194 if ( $result === 'continue' && !$this->output->headerDone() ) {
195 if ( $pageId !== false ) {
196 $this->happyPages[$pageId] = true;
197 }
198 $lowestUnhappy = $this->getLowestUnhappy();
199
200 if ( $this->request->getVal( 'lastPage' ) ) {
201 $nextPage = $this->request->getVal( 'lastPage' );
202 } elseif ( $pageId !== false ) {
203 $nextPage = $this->pageSequence[$pageId + 1];
204 } else {
205 $nextPage = $this->pageSequence[$lowestUnhappy];
206 }
207 if ( array_search( $nextPage, $this->pageSequence ) > $lowestUnhappy ) {
208 $nextPage = $this->pageSequence[$lowestUnhappy];
209 }
210 $this->output->redirect( $this->getUrl( array( 'page' => $nextPage ) ) );
211 }
212 return $this->finish();
213 }
214
215 public function getLowestUnhappy() {
216 if ( count( $this->happyPages ) == 0 ) {
217 return 0;
218 } else {
219 return max( array_keys( $this->happyPages ) ) + 1;
220 }
221 }
222
223 /**
224 * Start the PHP session. This may be called before execute() to start the PHP session.
225 */
226 public function startSession() {
227 $sessPath = $this->getSessionSavePath();
228 if( $sessPath != '' ) {
229 if( strval( ini_get( 'open_basedir' ) ) != '' ) {
230 // we need to skip the following check when open_basedir is on.
231 // The session path probably *wont* be writable by the current
232 // user, and telling them to change it is bad. Bug 23021.
233 } elseif( !is_dir( $sessPath ) || !is_writeable( $sessPath ) ) {
234 $this->showError( 'config-session-path-bad', $sessPath );
235 return false;
236 }
237 } else {
238 // If the path is unset it'll default to some system bit, which *probably* is ok...
239 // not sure how to actually get what will be used.
240 }
241 if( wfIniGetBool( 'session.auto_start' ) || session_id() ) {
242 // Done already
243 return true;
244 }
245
246 $this->phpErrors = array();
247 set_error_handler( array( $this, 'errorHandler' ) );
248 session_start();
249 restore_error_handler();
250 if ( $this->phpErrors ) {
251 $this->showError( 'config-session-error', $this->phpErrors[0] );
252 return false;
253 }
254 return true;
255 }
256
257 /**
258 * Get the value of session.save_path
259 *
260 * Per http://www.php.net/manual/en/session.configuration.php#ini.session.save-path,
261 * this might have some additional preceding parts which need to be
262 * ditched
263 *
264 * @return String
265 */
266 private function getSessionSavePath() {
267 $path = ini_get( 'session.save_path' );
268 $path = ltrim( substr( $path, strrpos( $path, ';' ) ), ';');
269
270 return $path;
271 }
272
273 /**
274 * Show an error message in a box. Parameters are like wfMsg().
275 */
276 public function showError( $msg /*...*/ ) {
277 $args = func_get_args();
278 array_shift( $args );
279 $args = array_map( 'htmlspecialchars', $args );
280 $msg = wfMsgReal( $msg, $args, false, false, false );
281 $this->output->addHTML( $this->getErrorBox( $msg ) );
282 }
283
284 /**
285 * Temporary error handler for session start debugging.
286 */
287 public function errorHandler( $errno, $errstr ) {
288 $this->phpErrors[] = $errstr;
289 }
290
291 /**
292 * Clean up from execute()
293 */
294 public function finish() {
295 $this->output->output();
296 $this->session['happyPages'] = $this->happyPages;
297 $this->session['skippedPages'] = $this->skippedPages;
298 $this->session['settings'] = $this->settings;
299 return $this->session;
300 }
301
302 /**
303 * Get a URL for submission back to the same script.
304 */
305 public function getUrl( $query = array() ) {
306 $url = $this->request->getRequestURL();
307 # Remove existing query
308 $url = preg_replace( '/\?.*$/', '', $url );
309 if ( $query ) {
310 $url .= '?' . wfArrayToCGI( $query );
311 }
312 return $url;
313 }
314
315 /**
316 * Get a WebInstallerPage from the main sequence, by ID.
317 */
318 public function getPageById( $id ) {
319 $pageName = $this->pageSequence[$id];
320 $pageClass = 'WebInstaller_' . $pageName;
321 return new $pageClass( $this );
322 }
323
324 /**
325 * Get a WebInstallerPage by name.
326 */
327 public function getPageByName( $pageName ) {
328 $pageClass = 'WebInstaller_' . $pageName;
329 return new $pageClass( $this );
330 }
331
332 /**
333 * Get a session variable.
334 */
335 public function getSession( $name, $default = null ) {
336 if ( !isset( $this->session[$name] ) ) {
337 return $default;
338 } else {
339 return $this->session[$name];
340 }
341 }
342
343 /**
344 * Set a session variable.
345 */
346 public function setSession( $name, $value ) {
347 $this->session[$name] = $value;
348 }
349
350 /**
351 * Get the next tabindex attribute value.
352 */
353 public function nextTabIndex() {
354 return $this->tabIndex++;
355 }
356
357 /**
358 * Initializes language-related variables.
359 */
360 public function setupLanguage() {
361 global $wgLang, $wgContLang, $wgLanguageCode;
362 if ( $this->getSession( 'test' ) === null && !$this->request->wasPosted() ) {
363 $wgLanguageCode = $this->getAcceptLanguage();
364 $wgLang = $wgContLang = Language::factory( $wgLanguageCode );
365 $this->setVar( 'wgLanguageCode', $wgLanguageCode );
366 $this->setVar( '_UserLang', $wgLanguageCode );
367 } else {
368 $wgLanguageCode = $this->getVar( 'wgLanguageCode' );
369 $wgLang = Language::factory( $this->getVar( '_UserLang' ) );
370 $wgContLang = Language::factory( $wgLanguageCode );
371 }
372 }
373
374 /**
375 * Retrieves MediaWiki language from Accept-Language HTTP header.
376 */
377 public function getAcceptLanguage() {
378 global $wgLanguageCode;
379
380 $mwLanguages = Language::getLanguageNames();
381 $langs = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
382 foreach ( explode( ';', $langs ) as $splitted ) {
383 foreach ( explode( ',', $splitted ) as $lang ) {
384 $lang = trim( strtolower( $lang ) );
385 if ( $lang == '' || $lang[0] == 'q' ) {
386 continue;
387 }
388 if ( isset( $mwLanguages[$lang] ) ) {
389 return $lang;
390 }
391 $lang = preg_replace( '/^(.*?)(?=-[^-]*)$/', '\\1', $lang );
392 if ( $lang != '' && isset( $mwLanguages[$lang] ) ) {
393 return $lang;
394 }
395 }
396 }
397 return $wgLanguageCode;
398 }
399
400 /**
401 * Called by execute() before page output starts, to show a page list.
402 */
403 public function startPageWrapper( $currentPageName ) {
404 $s = "<div class=\"config-page-wrapper\">\n" .
405 "<div class=\"config-page-list\"><ul>\n";
406 $lastHappy = -1;
407 foreach ( $this->pageSequence as $id => $pageName ) {
408 $happy = !empty( $this->happyPages[$id] );
409 $s .= $this->getPageListItem( $pageName,
410 $happy || $lastHappy == $id - 1, $currentPageName );
411 if ( $happy ) {
412 $lastHappy = $id;
413 }
414 }
415 $s .= "</ul><br/><ul>\n";
416 foreach ( $this->otherPages as $pageName ) {
417 $s .= $this->getPageListItem( $pageName, true, $currentPageName );
418 }
419 $s .= "</ul></div>\n". // end list pane
420 "<div class=\"config-page\">\n" .
421 Xml::element( 'h2', array(),
422 wfMsg( 'config-page-' . strtolower( $currentPageName ) ) );
423
424 $this->output->addHTMLNoFlush( $s );
425 }
426
427 /**
428 * Get a list item for the page list.
429 */
430 public function getPageListItem( $pageName, $enabled, $currentPageName ) {
431 $s = "<li class=\"config-page-list-item\">";
432 $name = wfMsg( 'config-page-' . strtolower( $pageName ) );
433 if ( $enabled ) {
434 $query = array( 'page' => $pageName );
435 if ( !in_array( $pageName, $this->pageSequence ) ) {
436 if ( in_array( $currentPageName, $this->pageSequence ) ) {
437 $query['lastPage'] = $currentPageName;
438 }
439 $link = Xml::element( 'a',
440 array(
441 'href' => $this->getUrl( $query )
442 ),
443 $name
444 );
445 } else {
446 $link = htmlspecialchars( $name );
447 }
448 if ( $pageName == $currentPageName ) {
449 $s .= "<span class=\"config-page-current\">$link</span>";
450 } else {
451 $s .= $link;
452 }
453 } else {
454 $s .= Xml::element( 'span',
455 array(
456 'class' => 'config-page-disabled'
457 ),
458 $name
459 );
460 }
461 $s .= "</li>\n";
462 return $s;
463 }
464
465 /**
466 * Output some stuff after a page is finished.
467 */
468 public function endPageWrapper() {
469 $this->output->addHTMLNoFlush(
470 "</div>\n" .
471 "<br style=\"clear:both\"/>\n" .
472 "</div>" );
473 }
474
475 /**
476 * Get HTML for an error box with an icon.
477 *
478 * @param $text String: wikitext, get this with wfMsgNoTrans()
479 */
480 public function getErrorBox( $text ) {
481 return $this->getInfoBox( $text, 'critical-32.png', 'config-error-box' );
482 }
483
484 /**
485 * Get HTML for a warning box with an icon.
486 *
487 * @param $text String: wikitext, get this with wfMsgNoTrans()
488 */
489 public function getWarningBox( $text ) {
490 return $this->getInfoBox( $text, 'warning-32.png', 'config-warning-box' );
491 }
492
493 /**
494 * Get HTML for an info box with an icon.
495 *
496 * @param $text String: wikitext, get this with wfMsgNoTrans()
497 * @param $icon String: icon name, file in skins/common/images
498 * @param $class String: additional class name to add to the wrapper div
499 */
500 public function getInfoBox( $text, $icon = 'info-32.png', $class = false ) {
501 $s =
502 "<div class=\"config-info $class\">\n" .
503 "<div class=\"config-info-left\">\n" .
504 Xml::element( 'img',
505 array(
506 'src' => '../skins/common/images/' . $icon,
507 'alt' => wfMsg( 'config-information' ),
508 )
509 ) . "\n" .
510 "</div>\n" .
511 "<div class=\"config-info-right\">\n" .
512 $this->parse( $text ) . "\n" .
513 "</div>\n" .
514 "<div style=\"clear: left;\"></div>\n" .
515 "</div>\n";
516 return $s;
517 }
518
519 /**
520 * Get small text indented help for a preceding form field.
521 * Parameters like wfMsg().
522 */
523 public function getHelpBox( $msg /*, ... */ ) {
524 $args = func_get_args();
525 array_shift( $args );
526 $args = array_map( 'htmlspecialchars', $args );
527 $text = wfMsgReal( $msg, $args, false, false, false );
528 $html = $this->parse( $text, true );
529 $id = $this->helpId++;
530 $alt = wfMsg( 'help' );
531
532 return
533 "<div class=\"config-help-wrapper\">\n" .
534 "<div class=\"config-help-message\">\n" .
535 $html .
536 "</div>\n" .
537 "<div class=\"config-show-help\">\n" .
538 "<a href=\"#\">" .
539 wfMsgHtml( 'config-show-help' ) .
540 "</a></div>\n" .
541 "<div class=\"config-hide-help\">\n" .
542 "<a href=\"#\">" .
543 wfMsgHtml( 'config-hide-help' ) .
544 "</a></div>\n</div>\n";
545 }
546
547 /**
548 * Output a help box.
549 */
550 public function showHelpBox( $msg /*, ... */ ) {
551 $args = func_get_args();
552 $html = call_user_func_array( array( $this, 'getHelpBox' ), $args );
553 $this->output->addHTML( $html );
554 }
555
556 /**
557 * Show a short informational message.
558 * Output looks like a list.
559 */
560 public function showMessage( $msg /*, ... */ ) {
561 $args = func_get_args();
562 array_shift( $args );
563 $html = '<div class="config-message">' .
564 $this->parse( wfMsgReal( $msg, $args, false, false, false ) ) .
565 "</div>\n";
566 $this->output->addHTML( $html );
567 }
568
569 /**
570 * Label a control by wrapping a config-input div around it and putting a
571 * label before it.
572 */
573 public function label( $msg, $forId, $contents ) {
574 if ( strval( $msg ) == '' ) {
575 $labelText = '&#160;';
576 } else {
577 $labelText = wfMsgHtml( $msg );
578 }
579 $attributes = array( 'class' => 'config-label' );
580 if ( $forId ) {
581 $attributes['for'] = $forId;
582 }
583 return
584 "<div class=\"config-input\">\n" .
585 Xml::tags( 'label',
586 $attributes,
587 $labelText ) . "\n" .
588 $contents .
589 "</div>\n";
590 }
591
592 /**
593 * Get a labelled text box to configure a variable.
594 *
595 * @param $params Array
596 * Parameters are:
597 * var: The variable to be configured (required)
598 * label: The message name for the label (required)
599 * attribs: Additional attributes for the input element (optional)
600 * controlName: The name for the input element (optional)
601 * value: The current value of the variable (optional)
602 */
603 public function getTextBox( $params ) {
604 if ( !isset( $params['controlName'] ) ) {
605 $params['controlName'] = 'config_' . $params['var'];
606 }
607 if ( !isset( $params['value'] ) ) {
608 $params['value'] = $this->getVar( $params['var'] );
609 }
610 if ( !isset( $params['attribs'] ) ) {
611 $params['attribs'] = array();
612 }
613 return
614 $this->label(
615 $params['label'],
616 $params['controlName'],
617 Xml::input(
618 $params['controlName'],
619 30, // intended to be overridden by CSS
620 $params['value'],
621 $params['attribs'] + array(
622 'id' => $params['controlName'],
623 'class' => 'config-input-text',
624 'tabindex' => $this->nextTabIndex()
625 )
626 )
627 );
628 }
629
630 /**
631 * Get a labelled password box to configure a variable.
632 *
633 * Implements password hiding
634 * @param $params Array
635 * Parameters are:
636 * var: The variable to be configured (required)
637 * label: The message name for the label (required)
638 * attribs: Additional attributes for the input element (optional)
639 * controlName: The name for the input element (optional)
640 * value: The current value of the variable (optional)
641 */
642 public function getPasswordBox( $params ) {
643 if ( !isset( $params['value'] ) ) {
644 $params['value'] = $this->getVar( $params['var'] );
645 }
646 if ( !isset( $params['attribs'] ) ) {
647 $params['attribs'] = array();
648 }
649 $params['value'] = $this->getFakePassword( $params['value'] );
650 $params['attribs']['type'] = 'password';
651 return $this->getTextBox( $params );
652 }
653
654 /**
655 * Get a labelled checkbox to configure a boolean variable.
656 *
657 * @param $params Array
658 * Parameters are:
659 * var: The variable to be configured (required)
660 * label: The message name for the label (required)
661 * attribs: Additional attributes for the input element (optional)
662 * controlName: The name for the input element (optional)
663 * value: The current value of the variable (optional)
664 */
665 public function getCheckBox( $params ) {
666 if ( !isset( $params['controlName'] ) ) {
667 $params['controlName'] = 'config_' . $params['var'];
668 }
669 if ( !isset( $params['value'] ) ) {
670 $params['value'] = $this->getVar( $params['var'] );
671 }
672 if ( !isset( $params['attribs'] ) ) {
673 $params['attribs'] = array();
674 }
675 if( isset( $params['rawtext'] ) ) {
676 $labelText = $params['rawtext'];
677 } else {
678 $labelText = $this->parse( wfMsg( $params['label'] ) );
679 }
680 return
681 "<div class=\"config-input-check\">\n" .
682 "<label>\n" .
683 Xml::check(
684 $params['controlName'],
685 $params['value'],
686 $params['attribs'] + array(
687 'id' => $params['controlName'],
688 'class' => 'config-input-text',
689 'tabindex' => $this->nextTabIndex(),
690 )
691 ) .
692 $labelText . "\n" .
693 "</label>\n" .
694 "</div>\n";
695 }
696
697 /**
698 * Get a set of labelled radio buttons.
699 *
700 * @param $params Array
701 * Parameters are:
702 * var: The variable to be configured (required)
703 * label: The message name for the label (required)
704 * itemLabelPrefix: The message name prefix for the item labels (required)
705 * values: List of allowed values (required)
706 * itemAttribs Array of attribute arrays, outer key is the value name (optional)
707 * commonAttribs Attribute array applied to all items
708 * controlName: The name for the input element (optional)
709 * value: The current value of the variable (optional)
710 */
711 public function getRadioSet( $params ) {
712 if ( !isset( $params['controlName'] ) ) {
713 $params['controlName'] = 'config_' . $params['var'];
714 }
715 if ( !isset( $params['value'] ) ) {
716 $params['value'] = $this->getVar( $params['var'] );
717 }
718 if ( !isset( $params['label'] ) ) {
719 $label = '';
720 } else {
721 $label = $this->parse( wfMsgNoTrans( $params['label'] ) );
722 }
723 $s = "<label class=\"config-label\">\n" .
724 $label .
725 "</label>\n" .
726 "<ul class=\"config-settings-block\">\n";
727 foreach ( $params['values'] as $value ) {
728 $itemAttribs = array();
729 if ( isset( $params['commonAttribs'] ) ) {
730 $itemAttribs = $params['commonAttribs'];
731 }
732 if ( isset( $params['itemAttribs'][$value] ) ) {
733 $itemAttribs = $params['itemAttribs'][$value] + $itemAttribs;
734 }
735 $checked = $value == $params['value'];
736 $id = $params['controlName'] . '_' . $value;
737 $itemAttribs['id'] = $id;
738 $itemAttribs['tabindex'] = $this->nextTabIndex();
739 $s .=
740 '<li>' .
741 Xml::radio( $params['controlName'], $value, $checked, $itemAttribs ) .
742 '&#160;' .
743 Xml::tags( 'label', array( 'for' => $id ), $this->parse(
744 wfMsgNoTrans( $params['itemLabelPrefix'] . strtolower( $value ) )
745 ) ) .
746 "</li>\n";
747 }
748 $s .= "</ul>\n";
749 return $s;
750 }
751
752 /**
753 * Output an error or warning box using a Status object.
754 */
755 public function showStatusBox( $status ) {
756 if( !$status->isGood() ) {
757 $text = $status->getWikiText();
758 if( $status->isOk() ) {
759 $box = $this->getWarningBox( $text );
760 } else {
761 $box = $this->getErrorBox( $text );
762 }
763 $this->output->addHTML( $box );
764 }
765 }
766
767 public function showStatusMessage( $status ) {
768 $text = $status->getWikiText();
769 $this->output->addWikiText(
770 "<div class=\"config-message\">\n" .
771 $text .
772 "</div>"
773 );
774 }
775
776 /**
777 * Convenience function to set variables based on form data.
778 * Assumes that variables containing "password" in the name are (potentially
779 * fake) passwords.
780 *
781 * @param $varNames Array
782 * @param $prefix String: the prefix added to variables to obtain form names
783 */
784 public function setVarsFromRequest( $varNames, $prefix = 'config_' ) {
785 $newValues = array();
786 foreach ( $varNames as $name ) {
787 $value = trim( $this->request->getVal( $prefix . $name ) );
788 $newValues[$name] = $value;
789 if ( $value === null ) {
790 // Checkbox?
791 $this->setVar( $name, false );
792 } else {
793 if ( stripos( $name, 'password' ) !== false ) {
794 $this->setPassword( $name, $value );
795 } else {
796 $this->setVar( $name, $value );
797 }
798 }
799 }
800 return $newValues;
801 }
802
803 /**
804 * Get the starting tags of a fieldset.
805 *
806 * @param $legend String: message name
807 */
808 public function getFieldsetStart( $legend ) {
809 return "\n<fieldset><legend>" . wfMsgHtml( $legend ) . "</legend>\n";
810 }
811
812 /**
813 * Get the end tag of a fieldset.
814 */
815 public function getFieldsetEnd() {
816 return "</fieldset>\n";
817 }
818
819 /**
820 * Helper for Installer::docLink()
821 */
822 public function getDocUrl( $page ) {
823 $url = "{$_SERVER['PHP_SELF']}?page=" . urlencode( $page );
824 if ( in_array( $this->currentPageName, $this->pageSequence ) ) {
825 $url .= '&lastPage=' . urlencode( $this->currentPageName );
826 }
827 return $url;
828 }
829 }
830
831 abstract class WebInstallerPage {
832 public function __construct( $parent ) {
833 $this->parent = $parent;
834 }
835
836 public function addHTML( $html ) {
837 $this->parent->output->addHTML( $html );
838 }
839
840 public function startForm() {
841 $this->addHTML(
842 "<div class=\"config-section\">\n" .
843 Xml::openElement(
844 'form',
845 array(
846 'method' => 'post',
847 'action' => $this->parent->getUrl( array( 'page' => $this->getName() ) )
848 )
849 ) . "\n"
850 );
851 }
852
853 public function endForm( $continue = 'continue' ) {
854 $this->parent->output->outputWarnings();
855 $s = "<div class=\"config-submit\">\n";
856 $id = $this->getId();
857 if ( $id === false ) {
858 $s .= Xml::hidden( 'lastPage', $this->parent->request->getVal( 'lastPage' ) );
859 }
860 if ( $continue ) {
861 // Fake submit button for enter keypress
862 $s .= Xml::submitButton( wfMsg( "config-$continue" ),
863 array( 'name' => "enter-$continue", 'style' => 'display:none' ) ) . "\n";
864 }
865 if ( $id !== 0 ) {
866 $s .= Xml::submitButton( wfMsg( 'config-back' ),
867 array(
868 'name' => 'submit-back',
869 'tabindex' => $this->parent->nextTabIndex()
870 ) ) . "\n";
871 }
872 if ( $continue ) {
873 $s .= Xml::submitButton( wfMsg( "config-$continue" ),
874 array(
875 'name' => "submit-$continue",
876 'tabindex' => $this->parent->nextTabIndex(),
877 ) ) . "\n";
878 }
879 $s .= "</div></form></div>\n";
880 $this->addHTML( $s );
881 }
882
883 public function getName() {
884 return str_replace( 'WebInstaller_', '', get_class( $this ) );
885 }
886
887 public function getId() {
888 return array_search( $this->getName(), $this->parent->pageSequence );
889 }
890
891 public abstract function execute();
892
893 public function getVar( $var ) {
894 return $this->parent->getVar( $var );
895 }
896
897 public function setVar( $name, $value ) {
898 $this->parent->setVar( $name, $value );
899 }
900 }
901
902 class WebInstaller_Language extends WebInstallerPage {
903 function execute() {
904 global $wgLang;
905 $r = $this->parent->request;
906 $userLang = $r->getVal( 'UserLang' );
907 $contLang = $r->getVal( 'ContLang' );
908
909 $lifetime = intval( ini_get( 'session.gc_maxlifetime' ) );
910 if ( !$lifetime ) {
911 $lifetime = 1440; // PHP default
912 }
913
914 if ( $r->wasPosted() ) {
915 # Do session test
916 if ( $this->parent->getSession( 'test' ) === null ) {
917 $requestTime = $r->getVal( 'LanguageRequestTime' );
918 if ( !$requestTime ) {
919 // The most likely explanation is that the user was knocked back
920 // from another page on POST due to session expiry
921 $msg = 'config-session-expired';
922 } elseif ( time() - $requestTime > $lifetime ) {
923 $msg = 'config-session-expired';
924 } else {
925 $msg = 'config-no-session';
926 }
927 $this->parent->showError( $msg, $wgLang->formatTimePeriod( $lifetime ) );
928 } else {
929 $languages = Language::getLanguageNames();
930 if ( isset( $languages[$userLang] ) ) {
931 $this->setVar( '_UserLang', $userLang );
932 }
933 if ( isset( $languages[$contLang] ) ) {
934 $this->setVar( 'wgLanguageCode', $contLang );
935 }
936 return 'continue';
937 }
938 } elseif ( $this->parent->showSessionWarning ) {
939 # The user was knocked back from another page to the start
940 # This probably indicates a session expiry
941 $this->parent->showError( 'config-session-expired', $wgLang->formatTimePeriod( $lifetime ) );
942 }
943
944 $this->parent->setSession( 'test', true );
945
946 if ( !isset( $languages[$userLang] ) ) {
947 $userLang = $this->getVar( '_UserLang', 'en' );
948 }
949 if ( !isset( $languages[$contLang] ) ) {
950 $contLang = $this->getVar( 'wgLanguageCode', 'en' );
951 }
952 $this->startForm();
953 $s =
954 Xml::hidden( 'LanguageRequestTime', time() ) .
955 $this->getLanguageSelector( 'UserLang', 'config-your-language', $userLang ) .
956 $this->parent->getHelpBox( 'config-your-language-help' ) .
957 $this->getLanguageSelector( 'ContLang', 'config-wiki-language', $contLang ) .
958 $this->parent->getHelpBox( 'config-wiki-language-help' );
959
960
961 $this->addHTML( $s );
962 $this->endForm();
963 }
964
965 /**
966 * Get a <select> for selecting languages
967 */
968 function getLanguageSelector( $name, $label, $selectedCode ) {
969 global $wgDummyLanguageCodes;
970 $s = Xml::openElement( 'select', array( 'id' => $name, 'name' => $name ) ) . "\n";
971
972 $languages = Language::getLanguageNames();
973 ksort( $languages );
974 $dummies = array_flip( $wgDummyLanguageCodes );
975 foreach ( $languages as $code => $lang ) {
976 if ( isset( $dummies[$code] ) ) continue;
977 $s .= "\n" . Xml::option( "$code - $lang", $code, $code == $selectedCode );
978 }
979 $s .= "\n</select>\n";
980 return $this->parent->label( $label, $name, $s );
981 }
982 }
983
984 class WebInstaller_Welcome extends WebInstallerPage {
985 function execute() {
986 if ( $this->parent->request->wasPosted() ) {
987 if ( $this->getVar( '_Environment' ) ) {
988 return 'continue';
989 }
990 }
991 $this->parent->output->addWikiText( wfMsgNoTrans( 'config-welcome' ) );
992 $status = $this->parent->doEnvironmentChecks();
993 if ( $status ) {
994 $this->parent->output->addWikiText( wfMsgNoTrans( 'config-copyright', wfMsg( 'config-authors' ) ) );
995 $this->startForm();
996 $this->endForm();
997 }
998 }
999 }
1000
1001 class WebInstaller_DBConnect extends WebInstallerPage {
1002 function execute() {
1003 $r = $this->parent->request;
1004 if ( $r->wasPosted() ) {
1005 $status = $this->submit();
1006 if ( $status->isGood() ) {
1007 $this->setVar( '_UpgradeDone', false );
1008 return 'continue';
1009 } else {
1010 $this->parent->showStatusBox( $status );
1011 }
1012 }
1013
1014
1015 $this->startForm();
1016
1017 $types = "<ul class=\"config-settings-block\">\n";
1018 $settings = '';
1019 $defaultType = $this->getVar( 'wgDBtype' );
1020 foreach ( $this->parent->getVar( '_CompiledDBs' ) as $type ) {
1021 $installer = $this->parent->getDBInstaller( $type );
1022 $types .=
1023 '<li>' .
1024 Xml::radioLabel(
1025 $installer->getReadableName(),
1026 'DBType',
1027 $type,
1028 "DBType_$type",
1029 $type == $defaultType,
1030 array( 'class' => 'dbRadio', 'rel' => "DB_wrapper_$type" )
1031 ) .
1032 "</li>\n";
1033
1034 $settings .=
1035 Xml::openElement( 'div', array( 'id' => 'DB_wrapper_' . $type, 'class' => 'dbWrapper' ) ) .
1036 Xml::element( 'h3', array(), wfMsg( 'config-header-' . $type ) ) .
1037 $installer->getConnectForm() .
1038 "</div>\n";
1039 }
1040 $types .= "</ul><br clear=\"left\"/>\n";
1041
1042 $this->addHTML(
1043 $this->parent->label( 'config-db-type', false, $types ) .
1044 $settings
1045 );
1046
1047 $this->endForm();
1048 }
1049
1050 function submit() {
1051 $r = $this->parent->request;
1052 $type = $r->getVal( 'DBType' );
1053 $this->setVar( 'wgDBtype', $type );
1054 $installer = $this->parent->getDBInstaller( $type );
1055 if ( !$installer ) {
1056 return Status::newFatal( 'config-invalid-db-type' );
1057 }
1058 return $installer->submitConnectForm();
1059 }
1060 }
1061
1062 class WebInstaller_Upgrade extends WebInstallerPage {
1063 function execute() {
1064 if ( $this->getVar( '_UpgradeDone' ) ) {
1065 if ( $this->parent->request->wasPosted() ) {
1066 // Done message acknowledged
1067 return 'continue';
1068 } else {
1069 // Back button click
1070 // Show the done message again
1071 // Make them click back again if they want to do the upgrade again
1072 $this->showDoneMessage();
1073 return 'output';
1074 }
1075 }
1076
1077 // wgDBtype is generally valid here because otherwise the previous page
1078 // (connect) wouldn't have declared its happiness
1079 $type = $this->getVar( 'wgDBtype' );
1080 $installer = $this->parent->getDBInstaller( $type );
1081
1082 if ( !$installer->needsUpgrade() ) {
1083 return 'skip';
1084 }
1085
1086 if ( $this->parent->request->wasPosted() ) {
1087 $this->addHTML(
1088 '<div id="config-spinner" style="display:none;"><img src="../skins/common/images/ajax-loader.gif" /></div>' .
1089 '<script>jQuery( "#config-spinner" )[0].style.display = "block";</script>' .
1090 '<textarea id="config-update-log" name="UpdateLog" rows="10" readonly="readonly">'
1091 );
1092 $this->parent->output->flush();
1093 $result = $installer->doUpgrade();
1094 $this->addHTML( '</textarea>
1095 <script>jQuery( "#config-spinner" )[0].style.display = "none";</script>' );
1096 $this->parent->output->flush();
1097 if ( $result ) {
1098 $this->setVar( '_UpgradeDone', true );
1099 $this->showDoneMessage();
1100 return 'output';
1101 }
1102 }
1103
1104 $this->startForm();
1105 $this->addHTML( $this->parent->getInfoBox(
1106 wfMsgNoTrans( 'config-can-upgrade', $GLOBALS['wgVersion'] ) ) );
1107 $this->endForm();
1108 }
1109
1110 function showDoneMessage() {
1111 $this->startForm();
1112 $this->addHTML(
1113 $this->parent->getInfoBox(
1114 wfMsgNoTrans( 'config-upgrade-done',
1115 $GLOBALS['wgServer'] .
1116 $this->getVar( 'wgScriptPath' ) . '/index' .
1117 $this->getVar( 'wgScriptExtension' )
1118 ), 'tick-32.png'
1119 )
1120 );
1121 $this->endForm( 'regenerate' );
1122 }
1123 }
1124
1125 class WebInstaller_DBSettings extends WebInstallerPage {
1126 function execute() {
1127 $installer = $this->parent->getDBInstaller( $this->getVar( 'wgDBtype' ) );
1128
1129 $r = $this->parent->request;
1130 if ( $r->wasPosted() ) {
1131 $status = $installer->submitSettingsForm();
1132 if ( $status === false ) {
1133 return 'skip';
1134 } elseif ( $status->isGood() ) {
1135 return 'continue';
1136 } else {
1137 $this->parent->showStatusBox( $status );
1138 }
1139 }
1140
1141 $form = $installer->getSettingsForm();
1142 if ( $form === false ) {
1143 return 'skip';
1144 }
1145
1146 $this->startForm();
1147 $this->addHTML( $form );
1148 $this->endForm();
1149 }
1150
1151 }
1152
1153 class WebInstaller_Name extends WebInstallerPage {
1154 function execute() {
1155 $r = $this->parent->request;
1156 if ( $r->wasPosted() ) {
1157 if ( $this->submit() ) {
1158 return 'continue';
1159 }
1160 }
1161
1162 $this->startForm();
1163
1164 if ( $this->getVar( 'wgSitename' ) == $GLOBALS['wgSitename'] ) {
1165 $this->setVar( 'wgSitename', '' );
1166 }
1167
1168 // Set wgMetaNamespace to something valid before we show the form.
1169 // $wgMetaNamespace defaults to $wgSiteName which is 'MediaWiki'
1170 $metaNS = $this->getVar( 'wgMetaNamespace' );
1171 $this->setVar( 'wgMetaNamespace', wfMsgForContent( 'config-ns-other-default' ) );
1172
1173 $this->addHTML(
1174 $this->parent->getTextBox( array(
1175 'var' => 'wgSitename',
1176 'label' => 'config-site-name',
1177 ) ) .
1178 $this->parent->getHelpBox( 'config-site-name-help' ) .
1179 $this->parent->getRadioSet( array(
1180 'var' => '_NamespaceType',
1181 'label' => 'config-project-namespace',
1182 'itemLabelPrefix' => 'config-ns-',
1183 'values' => array( 'site-name', 'generic', 'other' ),
1184 'commonAttribs' => array( 'class' => 'enableForOther', 'rel' => 'config_wgMetaNamespace' ),
1185 ) ) .
1186 $this->parent->getTextBox( array(
1187 'var' => 'wgMetaNamespace',
1188 'label' => '',
1189 'attribs' => array( 'disabled' => '' ),
1190 ) ) .
1191 $this->parent->getHelpBox( 'config-project-namespace-help' ) .
1192 $this->parent->getFieldsetStart( 'config-admin-box' ) .
1193 $this->parent->getTextBox( array(
1194 'var' => '_AdminName',
1195 'label' => 'config-admin-name'
1196 ) ) .
1197 $this->parent->getPasswordBox( array(
1198 'var' => '_AdminPassword',
1199 'label' => 'config-admin-password',
1200 ) ) .
1201 $this->parent->getPasswordBox( array(
1202 'var' => '_AdminPassword2',
1203 'label' => 'config-admin-password-confirm'
1204 ) ) .
1205 $this->parent->getHelpBox( 'config-admin-help' ) .
1206 $this->parent->getTextBox( array(
1207 'var' => '_AdminEmail',
1208 'label' => 'config-admin-email'
1209 ) ) .
1210 $this->parent->getHelpBox( 'config-admin-email-help' ) .
1211 $this->parent->getCheckBox( array(
1212 'var' => '_Subscribe',
1213 'label' => 'config-subscribe'
1214 ) ) .
1215 $this->parent->getHelpBox( 'config-subscribe-help' ) .
1216 $this->parent->getFieldsetEnd() .
1217 $this->parent->getInfoBox( wfMsg( 'config-almost-done' ) ) .
1218 $this->parent->getRadioSet( array(
1219 'var' => '_SkipOptional',
1220 'itemLabelPrefix' => 'config-optional-',
1221 'values' => array( 'continue', 'skip' )
1222 ) )
1223 );
1224
1225 // Restore the default value
1226 $this->setVar( 'wgMetaNamespace', $metaNS );
1227
1228 $this->endForm();
1229 return 'output';
1230 }
1231
1232 function submit() {
1233 $retVal = true;
1234 $this->parent->setVarsFromRequest( array( 'wgSitename', '_NamespaceType',
1235 '_AdminName', '_AdminPassword', '_AdminPassword2', '_AdminEmail',
1236 '_Subscribe', '_SkipOptional' ) );
1237
1238 // Validate site name
1239 if ( strval( $this->getVar( 'wgSitename' ) ) === '' ) {
1240 $this->parent->showError( 'config-site-name-blank' );
1241 $retVal = false;
1242 }
1243
1244 // Fetch namespace
1245 $nsType = $this->getVar( '_NamespaceType' );
1246 if ( $nsType == 'site-name' ) {
1247 $name = $this->getVar( 'wgSitename' );
1248 // Sanitize for namespace
1249 // This algorithm should match the JS one in WebInstallerOutput.php
1250 $name = preg_replace( '/[\[\]\{\}|#<>%+? ]/', '_', $name );
1251 $name = str_replace( '&', '&amp;', $name );
1252 $name = preg_replace( '/__+/', '_', $name );
1253 $name = ucfirst( trim( $name, '_' ) );
1254 } elseif ( $nsType == 'generic' ) {
1255 $name = wfMsg( 'config-ns-generic' );
1256 } else { // other
1257 $name = $this->getVar( 'wgMetaNamespace' );
1258 }
1259
1260 // Validate namespace
1261 if ( strpos( $name, ':' ) !== false ) {
1262 $good = false;
1263 } else {
1264 // Title-style validation
1265 $title = Title::newFromText( $name );
1266 if ( !$title ) {
1267 $good = $nsType == 'site-name' ? true : false;
1268 } else {
1269 $name = $title->getDBkey();
1270 $good = true;
1271 }
1272 }
1273 if ( !$good ) {
1274 $this->parent->showError( 'config-ns-invalid', $name );
1275 $retVal = false;
1276 }
1277 $this->setVar( 'wgMetaNamespace', $name );
1278
1279 // Validate username for creation
1280 $name = $this->getVar( '_AdminName' );
1281 if ( strval( $name ) === '' ) {
1282 $this->parent->showError( 'config-admin-name-blank' );
1283 $cname = $name;
1284 $retVal = false;
1285 } else {
1286 $cname = User::getCanonicalName( $name, 'creatable' );
1287 if ( $cname === false ) {
1288 $this->parent->showError( 'config-admin-name-invalid', $name );
1289 $retVal = false;
1290 } else {
1291 $this->setVar( '_AdminName', $cname );
1292 }
1293 }
1294
1295 // Validate password
1296 $msg = false;
1297 $pwd = $this->getVar( '_AdminPassword' );
1298 $user = User::newFromName( $cname );
1299 $valid = $user->getPasswordValidity( $pwd );
1300 if ( strval( $pwd ) === '' ) {
1301 # $user->getPasswordValidity just checks for $wgMinimalPasswordLength.
1302 # This message is more specific and helpful.
1303 $msg = 'config-admin-password-blank';
1304 } elseif ( $pwd !== $this->getVar( '_AdminPassword2' ) ) {
1305 $msg = 'config-admin-password-mismatch';
1306 } elseif ( $valid !== true ) {
1307 # As of writing this will only catch the username being e.g. 'FOO' and
1308 # the password 'foo'
1309 $msg = $valid;
1310 }
1311 if ( $msg !== false ) {
1312 $this->parent->showError( $msg );
1313 $this->setVar( '_AdminPassword', '' );
1314 $this->setVar( '_AdminPassword2', '' );
1315 $retVal = false;
1316 }
1317 return $retVal;
1318 }
1319 }
1320
1321 class WebInstaller_Options extends WebInstallerPage {
1322 function execute() {
1323 if ( $this->getVar( '_SkipOptional' ) == 'skip' ) {
1324 return 'skip';
1325 }
1326 if ( $this->parent->request->wasPosted() ) {
1327 if ( $this->submit() ) {
1328 return 'continue';
1329 }
1330 }
1331
1332 $this->startForm();
1333 $this->addHTML(
1334 # User Rights
1335 $this->parent->getRadioSet( array(
1336 'var' => '_RightsProfile',
1337 'label' => 'config-profile',
1338 'itemLabelPrefix' => 'config-profile-',
1339 'values' => array_keys( $this->parent->rightsProfiles ),
1340 ) ) .
1341 $this->parent->getHelpBox( 'config-profile-help' ) .
1342
1343 # Licensing
1344 $this->parent->getRadioSet( array(
1345 'var' => '_LicenseCode',
1346 'label' => 'config-license',
1347 'itemLabelPrefix' => 'config-license-',
1348 'values' => array_keys( $this->parent->licenses ),
1349 'commonAttribs' => array( 'class' => 'licenseRadio' ),
1350 ) ) .
1351 $this->getCCChooser() .
1352 $this->parent->getHelpBox( 'config-license-help' ) .
1353
1354 # E-mail
1355 $this->parent->getFieldsetStart( 'config-email-settings' ) .
1356 $this->parent->getCheckBox( array(
1357 'var' => 'wgEnableEmail',
1358 'label' => 'config-enable-email',
1359 'attribs' => array( 'class' => 'showHideRadio', 'rel' => 'emailwrapper' ),
1360 ) ) .
1361 $this->parent->getHelpBox( 'config-enable-email-help' ) .
1362 "<div id=\"emailwrapper\">" .
1363 $this->parent->getTextBox( array(
1364 'var' => 'wgPasswordSender',
1365 'label' => 'config-email-sender'
1366 ) ) .
1367 $this->parent->getHelpBox( 'config-email-sender-help' ) .
1368 $this->parent->getCheckBox( array(
1369 'var' => 'wgEnableUserEmail',
1370 'label' => 'config-email-user',
1371 ) ) .
1372 $this->parent->getHelpBox( 'config-email-user-help' ) .
1373 $this->parent->getCheckBox( array(
1374 'var' => 'wgEnotifUserTalk',
1375 'label' => 'config-email-usertalk',
1376 ) ) .
1377 $this->parent->getHelpBox( 'config-email-usertalk-help' ) .
1378 $this->parent->getCheckBox( array(
1379 'var' => 'wgEnotifWatchlist',
1380 'label' => 'config-email-watchlist',
1381 ) ) .
1382 $this->parent->getHelpBox( 'config-email-watchlist-help' ) .
1383 $this->parent->getCheckBox( array(
1384 'var' => 'wgEmailAuthentication',
1385 'label' => 'config-email-auth',
1386 ) ) .
1387 $this->parent->getHelpBox( 'config-email-auth-help' ) .
1388 "</div>" .
1389 $this->parent->getFieldsetEnd()
1390 );
1391
1392 $extensions = $this->parent->findExtensions();
1393 if( $extensions ) {
1394 $extHtml = $this->parent->getFieldsetStart( 'config-extensions' );
1395 foreach( array_keys($extensions) as $ext ) {
1396 $extHtml .= $this->parent->getCheckBox( array(
1397 'var' => "ext-$ext",
1398 'rawtext' => $ext,
1399 ) );
1400 }
1401 $extHtml .= $this->parent->getHelpBox( 'config-extensions-help' ) .
1402 $this->parent->getFieldsetEnd();
1403 $this->addHTML( $extHtml );
1404 }
1405
1406 $this->addHTML(
1407 # Uploading
1408 $this->parent->getFieldsetStart( 'config-upload-settings' ) .
1409 $this->parent->getCheckBox( array(
1410 'var' => 'wgEnableUploads',
1411 'label' => 'config-upload-enable',
1412 'attribs' => array( 'class' => 'showHideRadio', 'rel' => 'uploadwrapper' ),
1413 ) ) .
1414 $this->parent->getHelpBox( 'config-upload-help' ) .
1415 '<div id="uploadwrapper" style="display: none;">' .
1416 $this->parent->getTextBox( array(
1417 'var' => 'wgDeletedDirectory',
1418 'label' => 'config-upload-deleted',
1419 ) ) .
1420 $this->parent->getHelpBox( 'config-upload-deleted-help' ) .
1421 '</div>' .
1422 $this->parent->getTextBox( array(
1423 'var' => 'wgLogo',
1424 'label' => 'config-logo'
1425 ) ) .
1426 $this->parent->getHelpBox( 'config-logo-help' )
1427 );
1428 $canUse = $this->getVar( '_ExternalHTTP' ) ?
1429 'config-instantcommons-good' : 'config-instantcommons-bad';
1430 $this->addHTML(
1431 $this->parent->getCheckBox( array(
1432 'var' => 'wgUseInstantCommons',
1433 'label' => 'config-instantcommons',
1434 ) ) .
1435 $this->parent->getHelpBox( 'config-instantcommons-help', wfMsgNoTrans( $canUse ) ) .
1436 $this->parent->getFieldsetEnd()
1437 );
1438
1439 $caches = array( 'none' );
1440 if( count( $this->getVar( '_Caches' ) ) ) {
1441 $caches[] = 'accel';
1442 }
1443 $caches[] = 'memcached';
1444
1445 $this->addHTML(
1446 # Advanced settings
1447 $this->parent->getFieldsetStart( 'config-advanced-settings' ) .
1448 # Object cache settings
1449 $this->parent->getRadioSet( array(
1450 'var' => 'wgMainCacheType',
1451 'label' => 'config-cache-options',
1452 'itemLabelPrefix' => 'config-cache-',
1453 'values' => $caches,
1454 'value' => 'none',
1455 ) ) .
1456 $this->parent->getHelpBox( 'config-cache-help' ) .
1457 '<div id="config-memcachewrapper">' .
1458 $this->parent->getTextBox( array(
1459 'var' => '_MemCachedServers',
1460 'label' => 'config-memcached-servers',
1461 ) ) .
1462 $this->parent->getHelpBox( 'config-memcached-help' ) . '</div>' .
1463 $this->parent->getFieldsetEnd()
1464 );
1465 $this->endForm();
1466 }
1467
1468 function getCCPartnerUrl() {
1469 global $wgServer;
1470 $exitUrl = $wgServer . $this->parent->getUrl( array(
1471 'page' => 'Options',
1472 'SubmitCC' => 'indeed',
1473 'config__LicenseCode' => 'cc',
1474 'config_wgRightsUrl' => '[license_url]',
1475 'config_wgRightsText' => '[license_name]',
1476 'config_wgRightsIcon' => '[license_button]',
1477 ) );
1478 $styleUrl = $wgServer . dirname( dirname( $this->parent->getUrl() ) ) .
1479 '/skins/common/config-cc.css';
1480 $iframeUrl = 'http://creativecommons.org/license/?' .
1481 wfArrayToCGI( array(
1482 'partner' => 'MediaWiki',
1483 'exit_url' => $exitUrl,
1484 'lang' => $this->getVar( '_UserLang' ),
1485 'stylesheet' => $styleUrl,
1486 ) );
1487 return $iframeUrl;
1488 }
1489
1490 function getCCChooser() {
1491 $iframeAttribs = array(
1492 'class' => 'config-cc-iframe',
1493 'name' => 'config-cc-iframe',
1494 'id' => 'config-cc-iframe',
1495 'frameborder' => 0,
1496 'width' => '100%',
1497 'height' => '100%',
1498 );
1499 if ( $this->getVar( '_CCDone' ) ) {
1500 $iframeAttribs['src'] = $this->parent->getUrl( array( 'ShowCC' => 'yes' ) );
1501 } else {
1502 $iframeAttribs['src'] = $this->getCCPartnerUrl();
1503 }
1504
1505 return
1506 "<div class=\"config-cc-wrapper\" id=\"config-cc-wrapper\" style=\"display: none;\">\n" .
1507 Xml::element( 'iframe', $iframeAttribs, '', false /* not short */ ) .
1508 "</div>\n";
1509 }
1510
1511 function getCCDoneBox() {
1512 $js = "parent.document.getElementById('config-cc-wrapper').style.height = '$1';";
1513 // If you change this height, also change it in config.css
1514 $expandJs = str_replace( '$1', '54em', $js );
1515 $reduceJs = str_replace( '$1', '70px', $js );
1516 return
1517 '<p>'.
1518 Xml::element( 'img', array( 'src' => $this->getVar( 'wgRightsIcon' ) ) ) .
1519 '&#160;&#160;' .
1520 htmlspecialchars( $this->getVar( 'wgRightsText' ) ) .
1521 "</p>\n" .
1522 "<p style=\"text-align: center\">" .
1523 Xml::element( 'a',
1524 array(
1525 'href' => $this->getCCPartnerUrl(),
1526 'onclick' => $expandJs,
1527 ),
1528 wfMsg( 'config-cc-again' )
1529 ) .
1530 "</p>\n" .
1531 "<script type=\"text/javascript\">\n" .
1532 # Reduce the wrapper div height
1533 htmlspecialchars( $reduceJs ) .
1534 "\n" .
1535 "</script>\n";
1536 }
1537
1538
1539 function submitCC() {
1540 $newValues = $this->parent->setVarsFromRequest(
1541 array( 'wgRightsUrl', 'wgRightsText', 'wgRightsIcon' ) );
1542 if ( count( $newValues ) != 3 ) {
1543 $this->parent->showError( 'config-cc-error' );
1544 return;
1545 }
1546 $this->setVar( '_CCDone', true );
1547 $this->addHTML( $this->getCCDoneBox() );
1548 }
1549
1550 function submit() {
1551 $this->parent->setVarsFromRequest( array( '_RightsProfile', '_LicenseCode',
1552 'wgEnableEmail', 'wgPasswordSender', 'wgEnableUpload', 'wgLogo',
1553 'wgEnableUserEmail', 'wgEnotifUserTalk', 'wgEnotifWatchlist',
1554 'wgEmailAuthentication', 'wgMainCacheType', '_MemCachedServers',
1555 'wgUseInstantCommons' ) );
1556
1557 if ( !in_array( $this->getVar( '_RightsProfile' ),
1558 array_keys( $this->parent->rightsProfiles ) ) )
1559 {
1560 reset( $this->parent->rightsProfiles );
1561 $this->setVar( '_RightsProfile', key( $this->parent->rightsProfiles ) );
1562 }
1563
1564 $code = $this->getVar( '_LicenseCode' );
1565 if ( $code == 'cc-choose' ) {
1566 if ( !$this->getVar( '_CCDone' ) ) {
1567 $this->parent->showError( 'config-cc-not-chosen' );
1568 return false;
1569 }
1570 } elseif ( in_array( $code, array_keys( $this->parent->licenses ) ) ) {
1571 $entry = $this->parent->licenses[$code];
1572 if ( isset( $entry['text'] ) ) {
1573 $this->setVar( 'wgRightsText', $entry['text'] );
1574 } else {
1575 $this->setVar( 'wgRightsText', wfMsg( 'config-license-' . $code ) );
1576 }
1577 $this->setVar( 'wgRightsUrl', $entry['url'] );
1578 $this->setVar( 'wgRightsIcon', $entry['icon'] );
1579 } else {
1580 $this->setVar( 'wgRightsText', '' );
1581 $this->setVar( 'wgRightsUrl', '' );
1582 $this->setVar( 'wgRightsIcon', '' );
1583 }
1584
1585 $exts = $this->parent->getVar( '_Extensions' );
1586 foreach( $exts as $key => $ext ) {
1587 if( !$this->parent->request->getCheck( 'config_ext-' . $ext ) ) {
1588 unset( $exts[$key] );
1589 }
1590 }
1591 $this->parent->setVar( '_Extensions', $exts );
1592 return true;
1593 }
1594 }
1595
1596 class WebInstaller_Install extends WebInstallerPage {
1597
1598 function execute() {
1599 if( $this->parent->request->wasPosted() ) {
1600 return 'continue';
1601 } elseif( $this->getVar( '_InstallDone' ) ) {
1602 $this->startForm();
1603 $status = new Status();
1604 $status->warning( 'config-install-alreadydone' );
1605 $this->parent->showStatusBox( $status );
1606 } else {
1607 $this->startForm();
1608 $this->addHTML("<ul>");
1609 $this->parent->performInstallation(
1610 array( $this, 'startStage'),
1611 array( $this, 'endStage' )
1612 );
1613 $this->addHTML("</ul>");
1614 }
1615 $this->endForm();
1616 return true;
1617 }
1618
1619 public function startStage( $step ) {
1620 $this->addHTML( "<li>" . wfMsgHtml( "config-install-$step" ) . wfMsg( 'ellipsis') );
1621 }
1622
1623 public function endStage( $step, $status ) {
1624 $success = $status->isGood();
1625 $msg = $success ? 'config-install-step-done' : 'config-install-step-failed';
1626 $html = wfMsgHtml( 'word-separator' ) . wfMsgHtml( $msg );
1627 if ( !$success ) {
1628 $html = "<span class=\"error\">$html</span>";
1629 }
1630 $this->addHTML( $html . "</li>\n" );
1631 if( !$success ) {
1632 $this->parent->showStatusBox( $status );
1633 }
1634 }
1635 }
1636
1637 class WebInstaller_Complete extends WebInstallerPage {
1638 public function execute() {
1639 global $IP;
1640 $this->startForm();
1641 $this->addHTML(
1642 $this->parent->getInfoBox(
1643 wfMsgNoTrans( 'config-install-done',
1644 $GLOBALS['wgServer'] . $this->parent->getURL( array( 'localsettings' => 1 ) ),
1645 $GLOBALS['wgServer'] .
1646 $this->getVar( 'wgScriptPath' ) . '/index' .
1647 $this->getVar( 'wgScriptExtension' )
1648 ), 'tick-32.png'
1649 )
1650 );
1651 $this->endForm( false );
1652 }
1653 }
1654
1655 class WebInstaller_Restart extends WebInstallerPage {
1656 function execute() {
1657 $r = $this->parent->request;
1658 if ( $r->wasPosted() ) {
1659 $really = $r->getVal( 'submit-restart' );
1660 if ( $really ) {
1661 $this->parent->session = array();
1662 $this->parent->happyPages = array();
1663 $this->parent->settings = array();
1664 }
1665 return 'continue';
1666 }
1667
1668 $this->startForm();
1669 $s = $this->parent->getWarningBox( wfMsgNoTrans( 'config-help-restart' ) );
1670 $this->addHTML( $s );
1671 $this->endForm( 'restart' );
1672 }
1673 }
1674
1675 abstract class WebInstaller_Document extends WebInstallerPage {
1676 abstract function getFileName();
1677
1678 function execute() {
1679 $text = $this->getFileContents();
1680 $this->parent->output->addWikiText( $text );
1681 $this->startForm();
1682 $this->endForm( false );
1683 }
1684
1685 function getFileContents() {
1686 return file_get_contents( dirname( __FILE__ ) . '/../../' . $this->getFileName() );
1687 }
1688
1689 protected function formatTextFile( $text ) {
1690 $text = str_replace( array( '<', '{{', '[[' ),
1691 array( '&lt;', '&#123;&#123;', '&#91;&#91;' ), $text );
1692 // replace numbering with [1], [2], etc with MW-style numbering
1693 $text = preg_replace( "/\r?\n(\r?\n)?\\[\\d+\\]/m", "\\1#", $text );
1694 // join word-wrapped lines into one
1695 do {
1696 $prev = $text;
1697 $text = preg_replace( "/\n([\\*#])([^\r\n]*?)\r?\n([^\r\n#\\*:]+)/", "\n\\1\\2 \\3", $text );
1698 } while ( $text != $prev );
1699 // turn (bug nnnn) into links
1700 $text = preg_replace_callback('/bug (\d+)/', array( $this, 'replaceBugLinks' ), $text );
1701 // add links to manual to every global variable mentioned
1702 $text = preg_replace_callback('/(\$wg[a-z0-9_]+)/i', array( $this, 'replaceConfigLinks' ), $text );
1703 // special case for <pre> - formatted links
1704 do {
1705 $prev = $text;
1706 $text = preg_replace( '/^([^\\s].*?)\r?\n[\\s]+(https?:\/\/)/m', "\\1\n:\\2", $text );
1707 } while ( $text != $prev );
1708 return $text;
1709 }
1710
1711 private function replaceBugLinks( $matches ) {
1712 return '<span class="config-plainlink">[https://bugzilla.wikimedia.org/' .
1713 $matches[1] . ' bug ' . $matches[1] . ']</span>';
1714 }
1715
1716 private function replaceConfigLinks( $matches ) {
1717 return '<span class="config-plainlink">[http://www.mediawiki.org/wiki/Manual:' .
1718 $matches[1] . ' ' . $matches[1] . ']</span>';
1719 }
1720 }
1721
1722 class WebInstaller_Readme extends WebInstaller_Document {
1723 function getFileName() { return 'README'; }
1724
1725 function getFileContents() {
1726 return $this->formatTextFile( parent::getFileContents() );
1727 }
1728 }
1729
1730 class WebInstaller_ReleaseNotes extends WebInstaller_Document {
1731 function getFileName() { return 'RELEASE-NOTES'; }
1732
1733 function getFileContents() {
1734 return $this->formatTextFile( parent::getFileContents() );
1735 }
1736 }
1737
1738 class WebInstaller_UpgradeDoc extends WebInstaller_Document {
1739 function getFileName() { return 'UPGRADE'; }
1740
1741 function getFileContents() {
1742 return $this->formatTextFile( parent::getFileContents() );
1743 }
1744 }
1745
1746 class WebInstaller_Copying extends WebInstaller_Document {
1747 function getFileName() { return 'COPYING'; }
1748
1749 function getFileContents() {
1750 $text = parent::getFileContents();
1751 $text = str_replace( "\x0C", '', $text );
1752 $text = preg_replace_callback( '/\n[ \t]+/m', array( 'WebInstaller_Copying', 'replaceLeadingSpaces' ), $text );
1753 $text = '<tt>' . nl2br( $text ) . '</tt>';
1754 return $text;
1755 }
1756
1757 private static function replaceLeadingSpaces( $matches ) {
1758 return "\n" . str_repeat( '&#160;', strlen( $matches[0] ) );
1759 }
1760 }